package edu.hawaii.duedates;

import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
import edu.hawaii.duedates.configuration.ConfigurationManager;
import edu.hawaii.duedates.configuration.jaxb.Library;
import edu.hawaii.duedates.configuration.jaxb.User;
import edu.hawaii.duedates.library.HawaiiState;
import edu.hawaii.duedates.library.LenderComparator;
import edu.hawaii.duedates.library.UhManoa;

/**
 * Due Dates used to retrieve info on user's library books including title of books borrowed and due
 * date.
 * 
 * @author Phillip Ka Hung Lau
 * @author John Ly
 * @author Scheller Sanchez
 * @author Robin Raqueno
 */
public final class DueDates {
  /** List to store all items that are due. */
  private List<BorrowedItem> itemList = new ArrayList<BorrowedItem>();
  
  /** List of users that are within the XML file. */
  private final List<User> userList;
  
  /** Amount of milliseconds in a day. */
  private static final int MILLISECOND_DAY = 1000 * 60 * 60 * 24;
  
  /** Logged in user. */
  private User logInUser;
  
  /** Holder for SMTP server. */
  String smtp = "";
  
  /** Boolean holder to cancel. */
  boolean cancel = false;
  
  /** Boolean for uhm filter. */
  boolean uhmFilter = false;
  
  /** Boolean for hsl filter. */
  boolean hslFilter = false;
  
  /**
   * Empty constructor for DueDates.
   */
  public DueDates() {
    File defaultFile = new File(System.getProperty("user.home") + "/.duedates/duedates.xml");
    File testFile = new File(System.getProperty("user.dir") + "/xml/duedates.xml");
    // In case, defaultFile doesn't exists
    ConfigurationManager manager = new ConfigurationManager(testFile);
    // Overrride if defaultFile file does
    if (defaultFile.exists()) {
      manager = new ConfigurationManager(defaultFile);
    }
    this.userList = manager.getDueDates().getUsers().getUser();
    this.smtp = manager.getDueDates().getSmtpServer();
  }

  /**
   * Finds the correct user account within the XML.
   * @param args Command-line arguments with name and password.
   * @throws Exception If no user found.
   */
  public void findUser(String[] args) throws Exception {
    for (User user : this.userList) {
      if (user.getName().equals(args[0]) && user.getPassword().equals(args[1])) {
        this.logInUser = user;
      }
    }
    if (this.logInUser == null) {
      throw new Exception("The name and password does not match any of our records.");
    }
  }
  
  /**
   * This is the wake up method that will run a timer task.
   * 
   * @param d The interval of when to run the timer task.
   * @param email Checking to send to email.
   * @throws Exception If error in sending mail.
   */
  public void wakeUp(double d, final boolean email) throws Exception {
    if (d == 0) {
      if (email) {
        sendEmail();
      }
      else {
        retrieveInfo();
      }
      
    }
    
    else {
      
      Calendar cal = Calendar.getInstance(Locale.US);
      int val = (int) ((1 / d) * MILLISECOND_DAY);
      final Timer timer = new Timer();
      
      timer.schedule(new TimerTask() {
        public void run() {
        try {
          if (cancel) {
            timer.cancel();
          }
          if (email) {
            sendEmail();
          }
          else {
            retrieveInfo();
          }
          
        }
        catch (Exception e) {
          e.printStackTrace();
        }
      } }, cal.getTime(), val);
    } 
  }

  /**
   * Prints out the results stored in the list.
   * 
   * @return String containing all book information in the itemList.
   */
  public String printResults() {
    String output;
    if (this.itemList.isEmpty()) {
      return "\nNo Items Due";
    }
    else {
      String format = "%1$-14.14s | %2$-50.50s | %3$-10.10s | %4$-20.20s\n";
      output = "\n";
      output = output.concat(String.format(format, "Due Date", "Title", "Media Type", "Lender"));
      for (int j = 0; j < this.itemList.size(); j++) {
        output = output.concat(this.itemList.get(j).toString());
      }
      return output;
    }
  }

  /**
   * Looks at each library and retrieves the info.
   */
  public void retrieveInfo() {
    try {
      if (this.logInUser != null) {
        List<Library> libraries = this.logInUser.getLibrary();
        this.itemList.clear();
        for (Library lib : libraries) { 
          if ((lib.getName().equals("University of Hawaii")) && (!uhmFilter)) {
            retrieveUhManoaInfo(lib.getUsername(), lib.getPassword(), this.itemList);
            
          }
          if ((lib.getName().equals("Hawaii State Library")) && (!hslFilter)) {
            retrieveHawaiiStateInfo(lib.getUsername(), lib.getPassword(), this.itemList);
          }
        }
      }
    }
    
    catch (Exception e) {
      System.out.println(e.getMessage());
    }
    
  }

  /**
   * Login and retrieve checked out information from the UH Library web site.
   * 
   * @param id The 8-digit UH ID number.
   * @param lastName The last name that corresponds to the UH ID number.
   * @param itemList The list to add the borrowed items to.
   * @throws Exception If error logging in to UH library.
   */
  private void retrieveUhManoaInfo(String id, String lastName,
      List<BorrowedItem> itemList) throws Exception {
    UhManoa uhLibrary = new UhManoa();
    this.itemList = uhLibrary.getInfo(id, lastName, this.itemList);
  }

  /**
   * Login and retrieve checked out information from the Hawai'i State Library web site.
   * 
   * @param id The 10-digit bar code on the public library card.
   * @param password The password to the account (usually last 4 digit of phone number).
   * @param itemList The list to add the borrowed items to.
   * @throws Exception If error logging in to Hawai'i State library.
   */
  private void retrieveHawaiiStateInfo(String id, String password,
      List<BorrowedItem> itemList) throws Exception {
    HawaiiState hiLibrary = new HawaiiState();
    this.itemList = hiLibrary.getInfo(id, password, this.itemList);
  }

  /**
   * Creates a Mailer instance and send out an email with the list of books due.
   * @throws Exception If error sending the email.
   */
  public void sendEmail() throws Exception {
    Mailer mailer = new Mailer();
    mailer.send(this.logInUser.getEmail(), this.smtp, this.printResults());
    System.out.println("NAME: " + this.logInUser.getName());
    System.out.println("Email sent to: " + this.logInUser.getEmail());
    System.out.println("SMTP sentMail: " + this.smtp);
  }

  /**
   * Removes any books in the list that are beyond the within period specified by user.
   * 
   * @param list The list that holds all the books.
   * @param days The number of days from today to determine the period.
   * @return If error parsing.
   */
  public List<BorrowedItem> withinNumOfDays(List<BorrowedItem> list, int days) {
    Calendar dateWithin = Calendar.getInstance(Locale.US);
    dateWithin.set(Calendar.HOUR_OF_DAY, 23);
    dateWithin.set(Calendar.MINUTE, 59);
    dateWithin.set(Calendar.SECOND, 59);
    dateWithin.set(Calendar.MILLISECOND, 0);
    dateWithin.add(Calendar.DATE, days);
    for (int i = 0; i < list.size(); i++) {
      BorrowedItem book = list.get(i);
      // if book is beyond the within period
      if (book.getDueDate().compareTo(dateWithin) > 0) {
        list.remove(i--);
      }
    }
    return list;
  }

  /**
   * Sets the logged in user.
   * @param user The logged in user.
   */
  public void setUser(User user) {
    this.logInUser = user;
  }
  
  /**
   * Gets the logged in user.
   * 
   * @return The log in user.
   */
  public User getUser() {
    return this.logInUser;
  }
  
  /** 
   * Gets the itemList. 
   * @return The list containing all the BorrowedItems. 
   */ 
  public List<BorrowedItem> getItemList() { 
    return this.itemList; 
  } 
  
  /**
   * Sorts the item list.
   * @param sInput The sort option.
   */
  public void sortBy(String sInput) {
    if ("date".equals(sInput)) {
      // sort by Date
      Collections.sort(itemList);
    }
    else {
      //sort by Library
      Collections.sort(itemList, new LenderComparator());
    }
  }
  
  /**
   * Cancel the wake up method.
   */
  public void setCancelWakeUp() {
    this.cancel = true;
  }
  
  /**
   * Set retrieve only to get UHM items.
   */
  public void setLibraryFilterOutHsl() {
    this.uhmFilter = false;
    this.hslFilter = true;
  }
  
  /**
   * Set retrieve only to get HSL items.
   */
  public void setLibraryFilterOutUhm() {
    this.uhmFilter = true;
    this.hslFilter = false;
  }
  
  /**
   * Set retrieve from all libraries. 
   */
  public void setLibraryFilterNone() {
    this.uhmFilter = false;
    this.hslFilter = false;
  }
}
